<?php
// +----------------------------------------------------------------------
// | ThinkPHP
// +----------------------------------------------------------------------
// | Copyright (c) 2008 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id$

/**
 +------------------------------------------------------------------------------
 * ThinkPHP 视图基础类
 +------------------------------------------------------------------------------
 * @category   Think
 * @package  Think
 * @subpackage  Core
 * @author liu21st <liu21st@gmail.com>
 * @version  $Id$
 +------------------------------------------------------------------------------
 */
class View extends Base
{
    // 当前模板引擎类型
    protected $template =   null;
    // 模板输出变量
    private $tVar        =  array();
    // Trace输出变量
    private $trace        =   array();
    // 输出缓存
    protected $cache        =   false;

   /**
     +----------------------------------------------------------
     * 取得视图对象实例
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @return Template
     +----------------------------------------------------------
     */
    static function getInstance() {
        return Think::instance(__CLASS__);
    }

   /**
     +----------------------------------------------------------
     * 架构函数
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     */
    public function __construct()
    {
        // 视图初始化
        if(method_exists($this,'_init')) {
            $this->_init();
        }
    }

    /**
     +----------------------------------------------------------
     * 模板变量赋值
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param mixed $name
     * @param mixed $value
     +----------------------------------------------------------
     */
    public function assign($name,$value=''){
        if(is_array($name)) {
            $this->tVar   =  array_merge($this->tVar,$name);
        }elseif(is_object($name)){
            foreach($name as $key =>$val)
            {
                $this->tVar[$key] = $val;
            }
        }else {
            $this->tVar[$name] = $value;
        }
    }

    public function __set($name,$value) {
        $this->assign($name,$value);
    }

    /**
     +----------------------------------------------------------
     * 取得模板变量的值
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param string $name
     +----------------------------------------------------------
     * @return mixed
     +----------------------------------------------------------
     * @throws ThinkExecption
     +----------------------------------------------------------
     */
    public function get($name){
        if(isset($this->tVar[$name])) {
            return $this->tVar[$name];
        }else {
            return false;
        }
    }

    public function __get($name) {
        return $this->get($name);
    }

    /**
     +----------------------------------------------------------
     * Trace变量赋值
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param mixed $name
     * @param mixed $value
     +----------------------------------------------------------
     */
    public function trace($title,$value='') {
        if(is_array($title)) {
            $this->trace   =  array_merge($this->trace,$title);
        }else {
            $this->trace[$title] = $value;
        }
    }

    /**
     +----------------------------------------------------------
     * 设置输出缓存
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param bool $cache 是否使用输出缓存
     +----------------------------------------------------------
     * @return void
     +----------------------------------------------------------
     */
    public function setCache($cache)
    {
        $this->cache    =   $cache;
    }

    /**
     +----------------------------------------------------------
     * 加载模板和页面输出 可以返回输出内容
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param string $templateFile 模板文件名 留空为自动获取
     * @param string $charset 模板输出字符集
     * @param string $contentType 输出类型
     * @param string $varPrefix 模板变量前缀
     +----------------------------------------------------------
     * @return mixed
     +----------------------------------------------------------
     * @throws ThinkExecption
     +----------------------------------------------------------
     */
    public function display($templateFile='',$charset='',$contentType='text/html',$varPrefix='')
    {
        $this->fetch($templateFile,$charset,$contentType,$varPrefix,true);
    }

    /**
     +----------------------------------------------------------
     * 输出布局模板
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param string $layout 指定要输出的布局模板
     * @param string $charset 输出编码
     * @param string $contentType 输出类型
     * @param string $varPrefix 模板变量前缀
     * @param string $display 是否直接显示
     +----------------------------------------------------------
     * @return void
     +----------------------------------------------------------
     */
    public function layout($layoutFile,$charset='',$contentType='text/html',$varPrefix='',$display=true)
    {
        $GLOBALS['_viewStartTime'] = microtime(true);
        // 获取布局模板文件
        if(false === strpos($layoutFile,':')) {
            // 默认获取layout下面的布局文件
            // 如果不是需要另外指定 例如 Public:index
            $layoutFile  =  'Layout:'.$layoutFile;
        }
        // 获取布局模板文件
        $content    =   $this->fetch($layoutFile,$charset,$contentType,$varPrefix);
        // 查找布局包含的页面
        $find = preg_match_all('/<!-- layout::(.+?)::(.+?) -->/is',$content,$matches);
        if($find) {
            for ($i=0; $i< $find; $i++) {
                // 读取相关的页面模板替换布局单元
                if(0===strpos($matches[1][$i],'$')){
                    // 动态布局
                    $matches[1][$i]  =  $this->get(substr($matches[1][$i],1));
                }
                if(0 != $matches[2][$i]) {
                    // 检查布局缓存是否有效
                    $guid =  md5($matches[1][$i]);
                    $cache  =  Think::getCache($guid);
                    if($cache) {
                        $layoutContent = $cache;
                    }else{
                        $layoutContent = $this->fetch($matches[1][$i],$charset,$contentType,$varPrefix);
                        Think::setCache($guid,$layoutContent,$matches[2][$i]);
                    }
                }else{
                    $layoutContent = $this->fetch($matches[1][$i],$charset,$contentType,$varPrefix);
                }
                $content    =   str_replace($matches[0][$i],$layoutContent,$content);
            }
        }
        if($display) {
            $showTime   =   $this->showTime();
            echo $content;
            // 记录视图完成输出时间
            $GLOBALS['_viewEndTime'] = microtime(true);
            // 应用视图输出标签
            Tag::apply('view_end',$content,$charset,$contentType);
            // 显示页面执行时间
            if(C('SHOW_RUN_TIME')) {
                echo '<div  class="think_run_time">'.$showTime.'</div>';
            }
            // 显示页面Trace信息
            if(C('SHOW_PAGE_TRACE')) {
                $this->showTrace($charset);
            }
            return null;
        }else{
            return $content;
        }
        return ;
    }

    /**
     +----------------------------------------------------------
     * 加载模板和页面输出
     +----------------------------------------------------------
     * @access public
     +----------------------------------------------------------
     * @param string $templateFile 模板文件名 留空为自动获取
     * @param string $charset 模板输出字符集
     * @param string $contentType 输出类型
     * @param string $varPrefix 模板变量前缀
     * @param integer $mode 0 返回 1 输出 2 下载
     +----------------------------------------------------------
     * @throws ThinkExecption
     +----------------------------------------------------------
     */
    public function fetch($templateFile='',$charset='',$contentType='text/html',$varPrefix='',$display=false)
    {
        // 记录视图开始执行时间
        $GLOBALS['_viewStartTime'] = microtime(true);
        if(null===$templateFile) {
            // 使用null参数作为模版名直接返回不做任何输出
            return ;
        }
        if('layout::'==substr($templateFile,0,8)) {
            $this->layout(substr($templateFile,8));
            return ;
        }
        // 发送头部信息
        if(empty($charset)) $charset = C('OUTPUT_CHARSET');
        // 应用视图初始化标签
        Tag::apply('view_init',$templateFile,$charset,$contentType);
        // 开启页面缓存
        ob_start();
        ob_implicit_flush(0);

        //  模版解析
        if(''==$templateFile) {
            // 如果模板文件名为空 按照默认规则定位
            $templateFile = C('TMPL_FILE_NAME');
        }elseif(strpos($templateFile,'#')){
            // 引入组件的其他模块的操作模板 例如 User#Info:add
            $templateFile   =   LIB_PATH.str_replace(array('#',':'),array('/'.TMPL_DIR.'/'.TEMPLATE_NAME.'/','/'),$templateFile).C('TEMPLATE_SUFFIX');
        }elseif(strpos($templateFile,'@')){
            // 引入其它主题的操作模板 必须带上模块名称 例如 blue@User:add
            $templateFile   =   TMPL_PATH.str_replace(array('@',':'),'/',$templateFile).C('TEMPLATE_SUFFIX');
        }elseif(strpos($templateFile,':')){
            // 引入其它模块的操作模板
            $templateFile   =   TEMPLATE_PATH.'/'.str_replace(':','/',$templateFile).C('TEMPLATE_SUFFIX');
        }elseif(!file_exists($templateFile))    {
            // 引入当前模块的其它操作模板
            $templateFile =  dirname(C('TMPL_FILE_NAME')).'/'.$templateFile.C('TEMPLATE_SUFFIX');
        }
        if(!file_exists_case($templateFile)){
            throw_exception(L('_TEMPLATE_NOT_EXIST_').'['.$templateFile.']');
        }

        if(!Tag::exists('view_parse') ) {
            // 没有模板解析插件默认使用PHP模版
            include $templateFile;
        }else{
            // 调用模板引擎解析模板
            Tag::apply('view_parse',$templateFile,$this->tVar,$charset,$contentType,$varPrefix);
        }

        // 获取缓存内容并清空缓存
        $content = ob_get_clean();
        // 输出编码转换
        $content = auto_charset($content,C('TEMPLATE_CHARSET'),$charset);
        if(ini_get('output_handler') != 'ob_gzhandler' && ini_get('zlib.output_compression') == 0) {
            // 页面压缩输出
            if (stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
            {
                $compress = 'gzip';
                $content = gzencode($content, 3);
            }
            elseif (stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE)
            {
                $compress = 'deflate';
                $content = gzdeflate($content, 3);
            }
            header('Vary: Accept-Encoding');
            header('Content-Encoding: '.$compress);
            header('Content-Length: '.strlen($content));
        }

        // 应用视图输出标签
        Tag::apply('view_output',$content,$charset,$contentType);

        if($display) {
            $showTime   =   $this->showTime();
            echo $content;
            // 记录视图完成输出时间
            $GLOBALS['_viewEndTime'] = microtime(true);
            // 应用视图输出标签
            Tag::apply('view_end',$content,$charset,$contentType);
            // 显示页面执行时间
            if(C('SHOW_RUN_TIME')) {
                echo '<div  id="think_run_time" class="think_run_time">'.$showTime.'</div>';
            }
            // 显示页面Trace信息
            if(C('SHOW_PAGE_TRACE')) {
                $this->showTrace($charset);
            }
            return null;
        }else{
            return $content;
        }
    }

    protected function showTrace($charset) {
        // 显示页面Trace信息 读取Trace定义文件
        // 定义格式 return array('当前页面'=>$_SERVER['PHP_SELF'],'通信协议'=>$_SERVER['SERVER_PROTOCOL'],...);
        $traceFile  =   CONFIG_PATH.'trace.php';
         if(file_exists($traceFile)) {
            $_trace =   include $traceFile;
         }else{
            $_trace =   array();
         }
         // 系统默认显示信息
        $this->trace('当前页面',    $_SERVER['PHP_SELF']);
        $this->trace('请求方法',    $_SERVER['REQUEST_METHOD']);
        $this->trace('通信协议',    $_SERVER['SERVER_PROTOCOL']);
        $this->trace('请求时间',    date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']));
        $this->trace('用户代理',    $_SERVER['HTTP_USER_AGENT']);
        $this->trace('会话ID'   ,   session_id());
        $this->trace('输出编码',    $charset);
        if(isset(Log::$log[SQL_LOG_DEBUG])) {
            $log    =   Log::$log[SQL_LOG_DEBUG];
            $this->trace('SQL记录',is_array($log)?'['.count($log).']<br/>'.implode('<br/>',$log):'无SQL记录');
        }else{
            $this->trace('SQL记录','无SQL记录');
        }
        if(isset(Log::$log[WEB_LOG_ERROR])) {
            $log    =   Log::$log[WEB_LOG_ERROR];
            $this->trace('错误记录',    is_array($log)?'['.count($log).']<br/>'.implode('<br/>',$log):'无错误记录');
        }
        if(isset(Log::$log[WEB_LOG_DEBUG])) {
            $log    =   Log::$log[WEB_LOG_DEBUG];
            $this->trace('调试记录',    is_array($log)?'['.count($log).']<br/>'.implode('<br/>',$log):'无调试记录');
        }
        $_trace =   array_merge($_trace,$this->trace);
        $_trace = auto_charset($_trace,'utf-8');
        $_title =   auto_charset('页面Trace信息','utf-8');
        // 调用Trace页面模板
        include THINK_PATH.'/Tpl/PageTrace.tpl.php';
        return ;
    }

    /**
     +----------------------------------------------------------
     * 显示运行时间、数据库操作、缓存次数、内存使用信息
     +----------------------------------------------------------
     * @access protected
     +----------------------------------------------------------
     * @param string $startTime 开始时间
     +----------------------------------------------------------
     * @return string
     +----------------------------------------------------------
     * @throws ThinkExecption
     +----------------------------------------------------------
     */
    protected function showTime() {
        if(C('SHOW_RUN_TIME')) {
            // 显示运行时间
            $startTime   =   $GLOBALS['_viewStartTime'];
            $endTime = microtime(true);
            $total_run_time =   number_format(($endTime - $GLOBALS['_beginTime']), 3);
            $showTime   =   'Process: '.$total_run_time.'s ';
            if(C('SHOW_ADV_TIME')) {
                // 显示详细运行时间
                $_load_time =   number_format(($GLOBALS['_loadTime'] -$GLOBALS['_beginTime'] ), 3);
                $_init_time =   number_format(($GLOBALS['_initTime'] -$GLOBALS['_loadTime'] ), 3);
                $_exec_time =   number_format(($startTime  -$GLOBALS['_initTime'] ), 3);
                $_parse_time    =   number_format(($endTime - $startTime), 3);
                $showTime .= '( Load:'.$_load_time.'s Init:'.$_init_time.'s Exec:'.$_exec_time.'s Template:'.$_parse_time.'s )';
            }
            if(C('SHOW_DB_TIMES') && class_exists('Db',false) ) {
                // 显示数据库操作次数
                $db =   Db::getInstance();
                $showTime .= ' | DB :'.$db->Q().' queries '.$db->W().' writes ';
            }
            if(C('SHOW_CACHE_TIMES') && class_exists('Cache',false)) {
                // 显示数据库操作次数
                $cache  =   Cache::getInstance();
                $showTime .= ' | Cache :'.$cache->Q().' gets '.$cache->W().' writes ';
            }
            if(MEMORY_LIMIT_ON && C('SHOW_USE_MEM')) {
                // 显示内存开销
                $startMem    =  $GLOBALS['_startUseMems'];
                $endMem     =  memory_get_usage();
                $showTime .= ' | UseMem:'. number_format(($endMem - $startMem)/1024).' kb';
            }
            return $showTime;
        }
    }
}//
?>